/*
 * File:   Ecosystem.cpp
 * Author: Administrateur
 *
 * Created on 17 octobre 2008, 15:28
 */

#include<stdlib.h>
#include<iostream>
#include<vector>
#include"header/Ecosystem.h"
#include"header/TypeEntity.h"
#include"header/MapPredator.hpp"
#include"header/InformationException.h"
#include"rude/config.h"

/**
 *  Display the system with all resources
 **/
void Ecosystem::displaySystem() const {
    cout << "****************** EcoSystem ******************" << endl;
    for (int i = 0; i < length; ++i) {
        for (int j = 0; j < width; ++j) {
            try {
                cout << getEntityAt(i, j)->getRace() << "\t";
            } catch (std::exception& e) {
                cout << " \t";
            }
        }
        cout << std::endl;
    }
    cout << "***********************************************" << endl;
}

/**
 * Constructor
 */
Ecosystem::Ecosystem(int const& _length, int const& _width) : length(_length), width(_width) {
    /* initialize random seed: */
    srand(time(NULL));
}

/**
 * Generate random coordinates and insert in a new resource with specific parameters.
 */
Position Ecosystem::randomResource() const {
    int x, y;

    // Find a random empty cell
    do {
        x = rand() % length;
        y = rand() % width;
    } while (!Ecosystem::isAvailable(x, y));

    return Position(x, y);
}

/**
 *  Check if there is an entity at the position given in parameter
 */
bool Ecosystem::isAvailable(int const& x, int const& y) const {
    // We check that there is no animal at the position x,y
    int size;
    if (!vect_animal.empty()) {
        size = vect_animal.size();
        for (int i = 0; (i < size) && (vect_animal[i] != NULL); ++i) {
            if (vect_animal[i]->getPosition().isEqual(x, y))
                return false;
        }
    }

    // We also check that there is no vegetal at the position x,y
    if (!vect_vegetal.empty()) {
        size = vect_vegetal.size();
        for (int i = 0; i < size && vect_vegetal[i] != NULL; ++i) {
            if (vect_vegetal[i]->getPosition().isEqual(x, y))
                return false;
        }
    }
    return true;
}

/**
 * Start the ecosystem
 */
void Ecosystem::start() const {
    int j = 0;
    while (j++ < 2) {
        // For each animal & vegetal, we update her status
        int size;
        if (!vect_animal.empty()) {
            size = vect_animal.size();
            for (int i = 0; (i < size) && (vect_animal[i] != NULL); ++i) {
                vect_animal[i]->update();
            }
        }

        // We also check that there is no vegetal at the position x,y
        if (!vect_vegetal.empty()) {
            size = vect_vegetal.size();
            for (int i = 0; i < size && vect_vegetal[i] != NULL; ++i) {
                vect_vegetal[i]->update();
            }
        }
    }
}

/**
 * Getter for the entity at the position (x, y)
 */
Entity * Ecosystem::getEntityAt(int x, int y) const {
    if (x >= length) throw;
    if (y >= width) throw;

    // Search if the entity at the position (x,y) is an animal
    //  If it exists, we return it.
    int nbAnimals = vect_animal.size();
    for (int i = 0; i < nbAnimals; i++) {
        if ((*vect_animal[i]).getPosition().isEqual(x, y))
            return vect_animal[i];
    }

    // Search if the entity at the position (x,y) is a vegetal
    int nbVegetals = vect_vegetal.size();
    for (int i = 0; i < nbVegetals; i++) {
        if ((*vect_vegetal[i]).getPosition().isEqual(x, y))
            return vect_vegetal[i];
    }

    // If it is neither an animal nor vegetal, it means that there
    //  is no entity at the position (x,y)
    throw InformationException("No entity at (") << x << ", " << y << ")";
}

/**
 * Getter for the entities around the position (x, y).
 */
vector<Entity*> Ecosystem::getEntitiesAt(int x, int y, int fov) const {
    if (x >= length) throw;
    if (y >= width) throw;

    int minX = (x - fov >= 0) ? (x - fov) : 0;
    int maxX = (x + fov <= length) ? (x + fov) : length;
    int minY = (y - fov >= 0) ? (y - fov) : 0;
    int maxY = (y + fov <= width) ? (y + fov) : width;

    vector<Entity *> res;
    int nbAnimals = vect_animal.size();
    int nbVegetals = vect_vegetal.size();
    for (int _x = minX; _x <= maxX; _x++) {
        for (int _y = minY; _y <= maxY; _y++) {
            // Search if the entity at the position (x,y) is an animal
            for (int i = 0; i < nbAnimals; i++) {
                if ((*vect_animal[i]).getPosition().isEqual(_x, _y))
                    res.push_back(vect_animal[i]);
            }

            // Search if the entity at the position (x,y) is a vegetal
            for (int i = 0; i < nbVegetals; i++) {
                if ((*vect_vegetal[i]).getPosition().isEqual(_x, _y))
                    res.push_back(vect_vegetal[i]);
            }
        }
    }

    // If it is neither an animal nor a vegetal, it means that there
    //  is no entity at the position (x,y)
    if (res.empty())
        throw InformationException("No entities near (") << x << ", " << y << ")";
    else
        return res;

}

    /**
     *  Kill an entity of type <i>type</i> at the position (x, y)
     **/
    void Ecosystem::killEntityAt(int x, int y, TypeEntity type) {
        if (type == ANIMAL) {
            // Iterator
            std::vector<Animal*>::iterator it;
            //We find the animal to delete
            for (it=vect_animal.begin();it<vect_animal.end();it++) {
                if ((*it)->getPosition().isEqual(x, y)) {
                    delete (*it);
                    it = vect_animal.erase(it);
                }
                else
                    ++it;
            }
        } else if (type == VEGETAL) {
            // Iterator
            std::vector<Vegetal*>::iterator it;
            //We find the vegetal to delete
            for (it=vect_vegetal.begin();it<vect_vegetal.end();it++) {
                if ((*it)->getPosition().isEqual(x, y)) {
                    delete (*it);
                    it = vect_vegetal.erase(it);
                }
                else
                    ++it;
            }
        }
    }

/**
 * Create animals configured in the config file <i>configName</i>.
 * This config file contains:
 * 		- Type: Type of the animal
 * 		- Quantity: Quantity of the animal <i>type</i> to create
 * 		- PEscape: Percentage to escape during its turn
 * 		- PMove: Percentage to move during its turn
 * 		- PReproduce: Percentage to reproduce during its turn
 * 		- Tokens: Number of tokens
 * 		- MaxLife: Maximum lifepoint of the animal
 * 		- Fov: Field of view of the animal
 */
void Ecosystem::loadAnimals(char const* configName) {
    // Create config object
    rude::Config config;

    if (config.load(configName)) {
        int numAnimals = config.getNumSections(); // Count the number of animals

        // Create all animals
        for (int x = 1; x < numAnimals; x++) {
            config.setSection(config.getSectionNameAt(x)); // Animal at the section number X

            const char *typeAnimal = config.getStringValue("Type"); // Type of the animal

            int qtity = config.getIntValue("Quantity");
            float pEscape = config.getDoubleValue("PEscape");
            float pMove = config.getDoubleValue("PMove");
            float pReproduce = config.getDoubleValue("PReproduce");
            int tokens = config.getIntValue("Tokens");
            int maxLife = config.getIntValue("MaxLife");
            int fov = config.getIntValue("Fov");

            //Create the animal at a random position
            while (qtity-- > 0)
                vect_animal.push_back(new Animal(*this, Ecosystem::randomResource(), typeAnimal, pEscape, pMove, pReproduce, tokens, maxLife, fov));
        }
    } else {
        std::cout << "Error opening animals file: " << config.getError() << "\n";
    }
}

/**
 * Create vegetals configured in the config file <i>configName</i>.
 * This config file contains:
 * 		- Type: Type of the vegetal
 * 		- Quantity: Quantity of the vegetal <i>type</i> to create
 * 		- Expension: Factor of expension
 *
 */
void Ecosystem::loadVegetals(char const* configName) {
    // Create config object
    rude::Config config;

    if (config.load(configName)) {
        int numVegetals = config.getNumSections(); // Count the number of vegetals

        // Create all vegetals
        for (int x = 1; x < numVegetals; x++) {
            config.setSection(config.getSectionNameAt(x)); // Vegetal at the section number X

            const char *typeVegetal = config.getStringValue("Type"); // Type of the vegetal

            int qtity = config.getIntValue("Quantity");
            int expension = config.getIntValue("Expension");

            //Create the animal at a random position
            while (qtity-- > 0)
                vect_vegetal.push_back(new Vegetal(*this, Ecosystem::randomResource(), typeVegetal, expension));
        }
    } else {
        std::cout << "Error opening vegetals file: " << config.getError() << "\n";
    }
}

/**
 * Configure the map Predator --> vector of Preys
 * Syntax of the file:
 * 		[PREDATOR_NAME]
 * 		Prey1 = PREY_NAME_1
 * 		Prey2 = PREY_NAME_2
 * 		Prey3 = PREY_NAME_3
 * 		...
 */
void Ecosystem::loadPredators(char const* configName) {
    // Create config object
    rude::Config config;

    if (config.load(configName)) {
        int numPredators = config.getNumSections(); // Count the number of predators to define

        // Create the mapping Predator --> Preys
        for (int x = 1; x < numPredators; x++) {
            config.setSection(config.getSectionNameAt(x)); // Predator at the section number X

            int numPreys = config.getNumDataMembers();

            for (int i = 0; i < numPreys; i++) {
                const char *prey = config.getDataNameAt(i);
                const char *typePrey = config.getStringValue(prey); // Type of the prey

                // Add the prey
                mp.addPredator(config.getSectionNameAt(x), typePrey);
            }
        }
    } else {
        std::cout << "Error opening predators file: " << config.getError() << "\n";
    }
}


/**
 *  Add an animal to the ecosystem
 **/
void Ecosystem::addAnimal(string race, float pEscape, float pMove, float pReproduce, int tokens, int maxLife, int fov) {
    vect_animal.push_back(new Animal(*this, Ecosystem::randomResource(), race, pEscape, pMove, pReproduce, tokens, maxLife, fov));
}

/**
 *  Add a vegetal to the ecosystem
 **/
void Ecosystem::addVegetal(string race, int expension_factor) {
    vect_vegetal.push_back(new Vegetal(*this, Ecosystem::randomResource(), race, expension_factor));
}


/**
 *  Unit test for the Map predator.
 *  It adds the parameter pred as a predator and prey as a prey of the predator.
 *  Then, it tests if the pred is really identified as a predator for the prey.
 **/
bool Ecosystem::test_isPredator(string const& pred, string const& prey) {
    mp.addPredator(pred, prey);

    if (mp.isPredator(pred, prey)) {
        cout << "Predator: " << pred << " | " << "Prey: " << prey
             << " --> aaaaaaaaaah he wants to kill me!!!!!" << endl;
        return true;
    }
    else {
        cout << "Predator: " << pred << " | " << "Prey: " << prey
             << "Bug... It looks like my friend but he wants to kill me..." << endl;
        return false;
    }
}

/**
 *  Unit test for the method getEntityAt.
 *  It adds a vegetal at the position (x, y) and try to find it at this position.
 **/
bool Ecosystem::test_getEntityAt(int x, int y) {
    // Add a vegetal for test
    Position p(x,y);
    vect_vegetal.push_back(new Vegetal(*this, p, "VEGETAL_TEST", 1));
    cout << "A vegetal VEGETAL_TEST has been inserted at the position (" << x << ", " << y <<")" << endl;

    try {
        cout << "Found : " << getEntityAt(x, y)->getTypeEntity()
                << " Race: " << getEntityAt(x, y)->getRace()
                << " at the position (" << x << ", " << y <<")" << endl;
        return true;
    } catch (std::exception& e) {
        std::cout << e.what() << std::endl;
        return false;
    }
}



/*
 *
 */
int main(int argc, char** argv) {
    // Check if the user has entered the right number of arguments
    //  Param 1 : config of animals
    //  Param 2 : config of vegetals
    //  Param 3 : config of predators
    if (argc != 4) {
        Ecosystem eco(20, 20);
        //	eco.loadAnimals(argv[1]);
        //	eco.loadVegetals(argv[2]);
        //	eco.loadPredators(argv[3]);
        eco.loadAnimals("animals.ini");
        eco.loadVegetals("vegetals.ini");
        eco.loadPredators("predators.ini");

        // Test predator --> prey
        //  cout << "Test predator: " << eco.test_isPredator("Human", "cat") << endl;

        // Test EntityAt --> Works
        //  eco.test_getEntityAt(6,6);


        // Start the system
        eco.start();
    } else {
        cerr << "Usage : " << argv[0] << " [path_config_animals]  [path_config_vegetals]  [path_config_predators]" << endl;
    }

    return (EXIT_SUCCESS);
}



